﻿using DotNetCommon.Accessors;
using DotNetCommon.Data;
using DotNetCommon.Extensions;
using DotNetCommon.Logger;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Text.Json.Nodes;
using System.Threading;
using System.Threading.Tasks;

namespace DBUtil.Builders;

/// <summary>
/// 插入构造器(无实体定义)
/// </summary>
public class InsertBuilder : BaseBuilder
{
    protected static readonly ILogger<InsertBuilder> logger = LoggerFactory.CreateLogger<InsertBuilder>();
    public EnumInsertBuilderType Type { get; protected set; }
    public TableName TableName { get; internal set; }
    /// <summary>
    /// 可能是 
    /// <list type="bullet">
    /// <item>Dictionary&lt;string,object>: db.Insert("t_user",new {Name="jack",Age=10}.ToDictionary())</item>
    /// <item>IEnumerable&lt;Dictionary&lt;string,object>>: db.Insert("t_user",[new {Name="jack",Age=10}.ToDictionary()])</item>
    /// <item>Entity: db.Insert&lt;PersonEntity>(new PersonEntity()) 或 db.Insert&lt;PersonEntity>().SetEntity(new...)</item>
    /// <item>IEnumerable&lt;Entity>: db.Insert&lt;PersonEntity>([new PersonEntity()]) 或 db.Insert&lt;PersonEntity>().SetEntity([new...])</item>
    /// </list>
    /// </summary>
    public object Object { get; protected set; }
    internal InsertBuilder(DBAccess db, string tableName, object obj) : base(db)
    {
        Type = EnumInsertBuilderType.InsertByDictionary;
        Object = obj;
        TableName = db.ParseQuotedName(tableName);
    }
    #region batch
    protected int? rowsCountPerBatch = null;
    protected IsolationLevel? batchIsolationLevel = null;
    /// <summary>
    /// 当一次插入的数据太多时,尝试分批执行, 以防db报错
    /// <list type="bullet">
    /// <item>sqlserver: 最大可插入1000行</item>
    /// <item>mysql: 无最大行数限制, 但与server的通讯包有限制(ensure 'max_allowed_packet' is greater than...)</item>
    /// </list>
    /// </summary>
    public virtual InsertBuilder BatchOption(int? rowsCountPerBatch, IsolationLevel? batchIsolationLevel = null)
    {
        if (rowsCountPerBatch < 1) throw new Exception($"rowsCountPerBatch不能小于1!");
        this.rowsCountPerBatch = rowsCountPerBatch;
        this.batchIsolationLevel = batchIsolationLevel;
        return this;
    }
    #endregion
    #region 复写AsTable
    /// <summary>
    /// 更改生成的 Insert 语句中的表名, 如:
    /// <code>
    /// db.Insert("t_person").AsTable("t_person_202401")
    /// </code>
    /// </summary>
    public virtual InsertBuilder AsTable(string tableName)
    {
        TableName = db.ParseQuotedName(tableName);
        return this;
    }
    /// <summary>
    /// 更改生成的 Insert 语句中的表名, 如:
    /// <code>
    /// db.Insert("t_person").AsTableIf(DateTime.Now >= DateTime.Parse("2024-01-01"), "t_person_202401")
    /// </code>
    /// </summary>
    public virtual InsertBuilder AsTableIf(bool condition, string tableName)
        => condition ? AsTable(tableName) : this;
    /// <summary>
    /// 更改生成的 Insert 语句中的表名, 如:
    /// <code>
    /// db.Insert("t_person").AsTable(t => t + DateTime.Now.ToString("yyyyMM"))
    /// </code>
    /// </summary>
    public virtual InsertBuilder AsTable(Func<string, string> func)
    {
        var newTablePureName = func?.Invoke(TableName.Name);
        if (newTablePureName.IsNotNullOrWhiteSpace()) TableName.SetName(newTablePureName);
        return this;
    }
    /// <summary>
    /// 更改生成的 Insert 语句中的表名, 如:
    /// <code>
    /// db.Insert("t_person").AsTableIf(true, t => t + DateTime.Now.ToString("yyyyMM"))
    /// </code>
    /// </summary>
    public virtual InsertBuilder AsTableIf(bool condition, Func<string, string> func)
        => condition ? AsTable(func) : this;
    /// <summary>
    /// 更改生成的 Insert 语句中的表名, 如:
    /// <code>
    /// db.Insert("testdb.t_person").AsTable(tableName => tableName.SetName(tableName.Name + DateTime.Now.ToString("yyyyMM")))
    /// </code>
    /// </summary>
    public virtual InsertBuilder AsTable(Action<TableName> func)
    {
        func?.Invoke(TableName);
        return this;
    }
    /// <summary>
    /// 更改生成的 Insert 语句中的表名, 如:
    /// <code>
    /// db.Insert("testdb.t_person").AsTableIf(true, tableName => tableName.SetName(tableName.Name + DateTime.Now.ToString("yyyyMM")))
    /// </code>
    /// </summary>
    public virtual InsertBuilder AsTableIf(bool condition, Action<TableName> func)
        => condition ? AsTable(func) : this;
    #endregion
    #region 复写超时设置
    /// <summary>
    /// 设置sql语句执行超时时间
    /// </summary>
    public override InsertBuilder CommandTimeout(int timeoutSeconds)
        => base.CommandTimeout(timeoutSeconds) as InsertBuilder;
    /// <summary>
    /// 设置sql语句执行超时时间
    /// </summary>
    public override InsertBuilder CommandTimeoutIf(bool condition, int timeoutSeconds)
        => condition ? CommandTimeout(timeoutSeconds) : this;
    #endregion
    #region SetColumn & IgnoreColumns & OnlyColumns
    internal List<SetIgnoreItem> setIgnores = [];
    internal InsertBuilder ReplaceSetIgnores(List<SetIgnoreItem> newSetIgnores)
    {
        setIgnores = newSetIgnores;
        return this;
    }
    //todo: 支持写法 insert.SetColumn("person_count",()=>db.Select&lt;Person>().Count()
    private const string NO_EXPRESSION_MESSAGE = $"""不能在 insert 中使用表达式插入, 如果想插入 select 语句的结果, 可参考: insert.SetColumn("person_count",new RawString(db.Select<Person>().ToSqlCount()))!""";
    /// <summary>
    /// 示例: 
    /// <list type="bullet">
    /// <item>insert.SetColumn("create_time",DateTime.Now)</item>
    /// <item>insert.SetColumn("sex",EnumSex.Male)</item>
    /// <item>insert.SetColumn("score",99.99)</item>
    /// <item>insert.SetColumn("name","jack")</item>
    /// <item>insert.SetColumn("person_count",new RawString(db.Select&lt;Person>().ToSqlCount()))</item>
    /// </list>
    /// 注意: 下面是禁止的
    /// <list type="bullet">
    /// <item>不能使用表达式或委托, 如: insert.SetColumn("person_count",()=>db.Select&lt;Person>().Count())</item>
    /// </list>
    /// </summary>
    public virtual InsertBuilder SetColumn(string colname, object constantVal)
    {
        if (constantVal is Expression || constantVal is Delegate) throw new Exception(NO_EXPRESSION_MESSAGE);
        SetIgnoreItem.SetColumn(setIgnores, db.RemoveQuote(colname), constantVal);
        return this;
    }

    /// <summary>
    /// 示例: 
    /// <list type="bullet">
    /// <item>insert.SetColumnIf(true,"create_time",DateTime.Now)</item>
    /// <item>insert.SetColumnIf(true,"sex",EnumSex.Male)</item>
    /// <item>insert.SetColumnIf(1>0,"score",99.99)</item>
    /// <item>insert.SetColumnIf(true,"name","jack")</item>
    /// <item>insert.SetColumnIf(true,"person_count",new RawString(db.Select&lt;Person>().ToSqlCount()))</item>
    /// </list>
    /// 注意: 下面是禁止的
    /// <list type="bullet">
    /// <item>不能使用表达式或委托, 如: insert.SetColumnIf(true,"person_count",()=>db.Select&lt;Person>().Count())</item>
    /// </list>
    /// </summary>
    public virtual InsertBuilder SetColumnIf(bool condition, string colname, object constantVal)
        => condition ? SetColumn(colname, constantVal) : this;

    /// <summary>
    /// 忽略指定的列, 示例: 
    /// <list type="bullet">
    /// <item>insert.IgnoreColumns("name","age")</item>
    /// <item>insert.IgnoreColumns(new List&lt;string>{"name","age"})</item>
    /// </list>
    /// </summary>        
    public virtual InsertBuilder IgnoreColumns(params string[] colNames)
    {
        SetIgnoreItem.IgnoreColumns(setIgnores, colNames.Select(db.RemoveQuote).ToArray());
        return this;
    }
    /// <inheritdoc cref="InsertBuilder.IgnoreColumns(string[])" />
    public virtual InsertBuilder IgnoreColumns(IEnumerable<string> colNames)
    {
        SetIgnoreItem.IgnoreColumns(setIgnores, colNames.Select(db.RemoveQuote).ToArray());
        return this;
    }
    /// <summary>
    /// 如果 <c>condition</c> 为 <c>true</c> 则忽略指定的列, 示例:
    /// <list type="bullet">
    /// <item>insert.IgnoreColumnsIf(true,"name","age")</item>
    /// <item>insert.IgnoreColumnsIf(true,new List&lt;string>{"name","age"})</item>
    /// </list>
    /// </summary>
    public virtual InsertBuilder IgnoreColumnsIf(bool condition, params string[] colNames) => condition ? IgnoreColumns(colNames) : this;
    /// <inheritdoc cref="IgnoreColumnsIf(bool, string[])"/>
    public virtual InsertBuilder IgnoreColumnsIf(bool condition, IEnumerable<string> colNames) => condition ? IgnoreColumns(colNames) : this;
    /// <summary>
    /// 指定仅插入指定的列, 其他列将被忽略, 示例:
    /// <list type="bullet">
    /// <item>insert.OnlyColumns("name","age")</item>
    /// <item>insert.OnlyColumns(new List&lt;string>{"name","age"})</item>
    /// </list>
    /// </summary>
    public virtual InsertBuilder OnlyColumns(params string[] colNames)
    {
        SetIgnoreItem.OnlyColumns(setIgnores, colNames.Select(db.RemoveQuote).ToArray());
        return this;
    }
    /// <inheritdoc cref="InsertBuilder.OnlyColumns(string[])" />
    public virtual InsertBuilder OnlyColumns(IEnumerable<string> colNames)
    {
        SetIgnoreItem.OnlyColumns(setIgnores, colNames.Select(db.RemoveQuote).ToArray());
        return this;
    }
    /// <summary>
    /// 如果 <c>condition</c> 为 <c>true</c> 则仅插入指定的列, 示例:
    /// <list type="bullet">
    /// <item>insert.OnlyColumnsIf(true,"name","age")</item>
    /// <item>insert.OnlyColumnsIf(true,new List&lt;string>{"name","age"})</item>
    /// </list>
    /// 示例: 
    /// </summary>
    public virtual InsertBuilder OnlyColumnsIf(bool condition, params string[] colNames)
        => condition ? OnlyColumns(colNames) : this;
    /// <inheritdoc cref="OnlyColumnsIf(bool, string[])"/>
    public virtual InsertBuilder OnlyColumnsIf(bool condition, IEnumerable<string> colNames)
        => condition ? OnlyColumns(colNames) : this;
    #endregion        
    #region GetInsertedDatas
    /// <summary>
    /// 仅对插入的 header 进行过滤即可
    /// </summary>
    private void ApplyFilter(Dictionary<string, (bool affectAllRows, object value)> header)
    {
        for (int i = 0; i < setIgnores.Count; i++)
        {
            var setIgnoreItem = setIgnores[i];
            if (setIgnoreItem.Type == EnumSetIgnoreType.SetColumn)
            {
                header[setIgnoreItem.ColumnNamesPure[0]] = (true, setIgnoreItem.Value);
            }
            else if (setIgnoreItem.Type == EnumSetIgnoreType.IgnoreColumn)
            {
                for (int j = 0; j < setIgnoreItem.ColumnNamesPure.Length; j++)
                {
                    header.RemoveFluent(setIgnoreItem.ColumnNamesPure[j]);
                }
            }
            else if (setIgnoreItem.Type == EnumSetIgnoreType.OnlyColumn)
            {
                var removes = header.Keys.Where(i => !(setIgnoreItem.ColumnNamesPure.Contains(i))).ToList();
                foreach (var col in removes)
                {
                    header.RemoveFluent(col);
                }
            }
        }
    }

    /// <summary>
    /// header: 指示要插入哪些列, 以及是否影响到所有行<br/>
    /// setDics: 具体每行的数据
    /// </summary>
    private (Dictionary<string, (bool affectAllRows, object value)> header, List<Dictionary<string, object>> setDics) GetInsertedDatas()
    {
        List<Dictionary<string, object>> setDics;
        if (Object is null) setDics = [[]];
        else if (Object is List<Dictionary<string, object>> _setDics) setDics = _setDics;
        else if (Object is IEnumerable<Dictionary<string, object>> _setDics2) setDics = _setDics2.ToList();
        else if (Object is Dictionary<string, object> _setDics3) setDics = [_setDics3];
        else throw new Exception("初始类型不正确,应该是 Dictionary<string, object> 或 IEnumerable<Dictionary<string, object>>.");

        if (setDics.Count == 0) throw new Exception("没有发现要写入的数据!");
        //以 setDics 的第一行为基础
        var header = new Dictionary<string, (bool affectAllRows, object value)>(setDics[0].Select(i => new KeyValuePair<string, (bool affectAllRows, object value)>(i.Key, (false, i.Value))));
        ApplyFilter(header);
        return (header, setDics);
    }
    #endregion

    #region 执行
    /// <summary>
    /// 插入并返回自增主键
    /// </summary>
    public virtual TKey ExecuteIdentity<TKey>()
    {
        var sqls = ToSqlMulti(EnumInsertToSql.ExecuteIdentity);
        if (sqls.Count > 1)
        {
            return db.RunInTransaction(() =>
            {
                for (int i = 0; i < sqls.Count - 1; i++)
                {
                    RunWriteMonitor(new AfterWriteArgument
                    {
                        TableName = TableName.FullNameQuoted,
                        WriteType = EnumWriteType.Insert
                    }, () => db.ExecuteSql(sqls[i], CommandType.Text, TimeoutSeconds));
                }
                return RunWriteMonitor(new AfterWriteArgument
                {
                    TableName = TableName.FullNameQuoted,
                    WriteType = EnumWriteType.Insert
                }, () => db.SelectScalar<TKey>(sqls[^1], CommandType.Text, TimeoutSeconds));
            }, batchIsolationLevel);
        }
        else
        {
            return RunWriteMonitor(new AfterWriteArgument
            {
                TableName = TableName.FullNameQuoted,
                WriteType = EnumWriteType.Insert
            }, () => db.SelectScalar<TKey>(sqls[0], CommandType.Text, TimeoutSeconds));
        }
    }

    /// <summary>
    /// 插入并返回自增主键
    /// </summary>
    public virtual async Task<TKey> ExecuteIdentityAsync<TKey>(CancellationToken cancellationToken = default)
    {
        var sqls = ToSqlMulti(EnumInsertToSql.ExecuteIdentity);
        if (sqls.Count > 1)
        {
            return await db.RunInTransactionAsync(async () =>
            {
                for (int i = 0; i < sqls.Count - 1; i++)
                {
                    await RunWriteMonitorAsync(new AfterWriteArgument
                    {
                        TableName = TableName.FullNameQuoted,
                        WriteType = EnumWriteType.Insert
                    }, async () => await db.ExecuteSqlAsync(sqls[i], CommandType.Text, TimeoutSeconds, null, cancellationToken));
                }
                return await RunWriteMonitorAsync(new AfterWriteArgument
                {
                    TableName = TableName.FullNameQuoted,
                    WriteType = EnumWriteType.Insert
                }, async () => await db.SelectScalarAsync<TKey>(sqls[^1], CommandType.Text, TimeoutSeconds, null, cancellationToken));
            }, batchIsolationLevel);
        }
        else
        {
            return await RunWriteMonitorAsync(new AfterWriteArgument
            {
                TableName = TableName.FullNameQuoted,
                WriteType = EnumWriteType.Insert
            }, async () => await db.SelectScalarAsync<TKey>(sqls[0], CommandType.Text, TimeoutSeconds, null, cancellationToken));
        }
    }

    /// <summary>
    /// 插入并返回自增主键
    /// </summary>
    public virtual int ExecuteIdentity() => ExecuteIdentity<int>();

    /// <summary>
    /// 插入并返回自增主键
    /// </summary>
    public virtual async Task<int> ExecuteIdentityAsync(CancellationToken cancellationToken = default)
        => await ExecuteIdentityAsync<int>(cancellationToken);

    /// <summary>
    /// 插入并返回自增主键
    /// </summary>
    public virtual long ExecuteIdentityLong() => ExecuteIdentity<long>();

    /// <summary>
    /// 插入并返回自增主键
    /// </summary>
    public virtual async Task<long> ExecuteIdentityLongAsync(CancellationToken cancellationToken = default)
        => await ExecuteIdentityAsync<long>(cancellationToken);

    /// <summary>
    /// 插入并返回受影响的行数
    /// </summary>
    public virtual int ExecuteAffrows()
    {
        var sqls = ToSqlMulti(EnumInsertToSql.ExecuteAffrows);
        if (sqls.Count > 1)
        {
            return db.RunInTransaction(() =>
            {
                var count = 0;
                for (int i = 0; i < sqls.Count; i++)
                {
                    count += RunWriteMonitor(new AfterWriteArgument
                    {
                        TableName = TableName.FullNameQuoted,
                        WriteType = EnumWriteType.Insert
                    }, () => db.ExecuteSql(sqls[i], CommandType.Text, TimeoutSeconds));
                }
                return count;
            }, batchIsolationLevel);
        }
        else
        {
            return RunWriteMonitor(new AfterWriteArgument
            {
                TableName = TableName.FullNameQuoted,
                WriteType = EnumWriteType.Insert
            }, () => db.ExecuteSql(sqls[0], CommandType.Text, TimeoutSeconds));
        }
    }

    /// <summary>
    /// 插入并返回受影响的行数
    /// </summary>
    public virtual async Task<int> ExecuteAffrowsAsync(CancellationToken cancellationToken = default)
    {
        var sqls = ToSqlMulti(EnumInsertToSql.ExecuteAffrows);
        if (sqls.Count > 1)
        {
            return await db.RunInTransactionAsync(async () =>
            {
                var count = 0;
                for (int i = 0; i < sqls.Count; i++)
                {
                    count += await RunWriteMonitorAsync(new AfterWriteArgument
                    {
                        TableName = TableName.FullNameQuoted,
                        WriteType = EnumWriteType.Insert
                    }, async () => await db.ExecuteSqlAsync(sqls[i], CommandType.Text, TimeoutSeconds, null, cancellationToken));
                }
                return count;
            }, batchIsolationLevel);
        }
        else
        {
            return await RunWriteMonitorAsync(new AfterWriteArgument
            {
                TableName = TableName.FullNameQuoted,
                WriteType = EnumWriteType.Insert
            }, async () => await db.ExecuteSqlAsync(sqls[0], CommandType.Text, TimeoutSeconds, null, cancellationToken));
        }
    }
    #endregion

    /// <summary>
    /// 便于 BulkCopy, 如果设置了 RawString 则会被提前执行, 如:
    /// <code>
    /// db.Insert("t_user").SetColumn("count",new RawString("select 1"))
    /// </code>
    /// 为了便于组装 DataTable 将会首先执行 "select 1"
    /// </summary>
    public virtual DataTable ToDataTable()
    {
        var dt = new DataTable { TableName = TableName.Name };
        var (header, setDics) = GetInsertedDatas();
        var colType = typeof(object);
        //都需要去掉 quote
        foreach (var item in header) dt.Columns.Add(db.RemoveQuote(item.Key), colType);
        for (int i = 0; i < setDics.Count; i++)
        {
            var setDic = setDics[i];
            var row = dt.NewRow();
            dt.Rows.Add(row);
            foreach (var item in header)
            {
                if (item.Value.affectAllRows)
                {
                    row[db.RemoveQuote(item.Key)] = item.Value.value;
                }
                else
                {
                    row[db.RemoveQuote(item.Key)] = setDic[item.Key];
                }
            }
        }
        return dt;
    }

    public virtual string ToSql(EnumInsertToSql enumInsertToSql) => ToSql(enumInsertToSql, false).First();
    public virtual string ToSql() => ToSql(EnumInsertToSql.ExecuteAffrows, false).First();
    public virtual List<string> ToSqlMulti(EnumInsertToSql enumInsertToSql) => ToSql(enumInsertToSql, true);
    public virtual List<string> ToSqlMulti() => ToSql(EnumInsertToSql.ExecuteAffrows, true);

    /// <summary>
    /// 返回生成的sql
    /// </summary>
    private List<string> ToSql(EnumInsertToSql enumInsertToSql, bool multi)
    {
        if (enumInsertToSql == EnumInsertToSql.ExecuteInserted) throw new Exception($"{Type}模式无法获取新增的数据!");
        if (TableName?.Name.IsNullOrWhiteSpace() == true) throw new Exception($"必须先指定 TableName, 参照: db.Insert(\"test\") 或 insert.AsTable(...)");
        var (header, setDics) = GetInsertedDatas();
        var len = setDics.Count;

        var sbHeader = new StringBuilder();
        sbHeader.Append("insert into ").Append(TableName.FullNameQuoted).Append('(');
        var headerIdx = 0;
        foreach (var item in header)
        {
            if (headerIdx > 0) sbHeader.Append(',');
            sbHeader.Append(db.ParseQuotedName(item.Key).FullNameQuoted);
            headerIdx++;
        }
        sbHeader.Append(") values");

        //分批次
        var rowsCountPerBatch = multi == false ? int.MaxValue : (this.rowsCountPerBatch ?? db.InsertRowsMaxCountPerBatch);
        var batchCount = setDics.Count / rowsCountPerBatch + (setDics.Count % rowsCountPerBatch > 0 ? 1 : 0);
        //每一个 sbBatch: (1,'jack'),\r\n(2,'tom');
        var sbBatchs = new StringBuilder[batchCount];
        for (int i = 0; i < sbBatchs.Length; i++) sbBatchs[i] = new StringBuilder();

        for (int r = 0; r < len; r++)//每一行
        {
            //先做 batch 计算
            //假设 len=11 rowsCountPerBatch=5
            //即: 0 1 2 3 4      5 6 7 8 9       10
            //那么 r=6 时,  batchIndex=1; sbBatch 是第二个 batch; 当前batch有 5 行数据
            //那么 r=10 时, batchIndex=2; sbBatch 是第三个 batch; 当前batch有 1 行数据
            var batchIndex = r / rowsCountPerBatch;//第几个batch
            var sbBatch = sbBatchs[batchIndex];//当前所属batch
            var isBatchFirstRow = r % rowsCountPerBatch == 0;//是否是batch内第一行
            var isBatchSecondRow = r % rowsCountPerBatch == 1;//是否是batch内第二行
            var isBatchEndRow = r % rowsCountPerBatch == rowsCountPerBatch - 1 || r == len - 1;//是否是batch内最后一行

            if (isBatchSecondRow)
            {
                //第二行的时候控制第一行换行缩进
                sbBatch.Insert(0, "\r\n    ");
                sbBatch.Append("\r\n    ");
            }
            else if (!isBatchFirstRow)
            {
                //不是第二行也不是第一行 自己换行缩进
                sbBatch.Append("\r\n    ");
            }
            sbBatch.Append('(');

            var setDic = setDics[r];//当前行字典
            var colIndex = 0;//进行的列
            foreach (var item in header)
            {
                //非第一列, 需要加入逗号
                if (colIndex > 0) sbBatch.Append(',');
                if (item.Value.affectAllRows)
                {
                    sbBatch.Append(db.ConvertToSqlSeg(item.Value.value).UnWrap());
                }
                else
                {
                    sbBatch.Append(db.ConvertToSqlSeg(setDic[item.Key]).UnWrap());
                }
                colIndex++;
            }
            //行结束
            sbBatch.Append(')');
            if (isBatchEndRow) sbBatch.Append(';');
            else sbBatch.Append(',');
        }

        var sbTail = new StringBuilder();
        if (enumInsertToSql == EnumInsertToSql.ExecuteIdentity)
        {
            sbTail.Append("\r\n").Append($"select {db.GetLastInsertedIdSeg()}{(setDics.Count > 1 ? $"+{setDics.Count - 1}" : "")};");
        }
        //合并 sbRows 到 sbHeader
        if (sbBatchs.Length > 1)
        {
            //分批次
            var sqls = new List<string>(sbBatchs.Length);
            for (var i = 0; i < sbBatchs.Length; i++)
            {
                var sbBatch = sbBatchs[i];
                sbBatch.Insert(0, sbHeader);
                if (i == sbBatchs.Length - 1) sbBatch.Append(sbTail);
                sqls.Add(sbBatch.ToString());
                sbBatch.Clear();
            }
            sbHeader.Clear(); sbTail.Clear();
            return sqls;
        }
        else
        {
            sbHeader.Append(sbBatchs[0]);
            sbHeader.Append(sbTail);
            var sql = sbHeader.ToString();
            sbHeader.Clear(); sbTail.Clear(); sbBatchs[0].Clear();
            return [sql];
        }
    }

}

/// <summary>
/// 插入构造器(有实体定义)
/// </summary>
public class InsertBuilder<T> : InsertBuilder where T : class, new()
{
    protected static new readonly ILogger<InsertBuilder<T>> logger = LoggerFactory.CreateLogger<InsertBuilder<T>>();
    private EntityInfo EntityInfo { get; set; }
    #region 初始化
    internal InsertBuilder(DBAccess db, params T[] entities) : base(db, db.GetEntityInfoInternal<T>().TableName.FullNameQuoted, entities)
    {
        EntityInfo = db.GetEntityInfoInternal(typeof(T), false);
        Type = entities.IsNullOrEmpty() ? EnumInsertBuilderType.InsertByDto : EnumInsertBuilderType.InsertByEntity;
    }
    internal InsertBuilder(DBAccess db, IEnumerable<T> entities) : base(db, db.GetEntityInfoInternal<T>().TableName.FullNameQuoted, entities)
    {
        EntityInfo = db.GetEntityInfoInternal(typeof(T), false);
        Type = entities.IsNullOrEmpty() ? EnumInsertBuilderType.InsertByDto : EnumInsertBuilderType.InsertByEntity;
    }
    /// <summary>
    /// 设置实体, 如:
    /// <code>
    /// db.Insert&lt;PersonEntity>().SetEntity(new PersonEntity{...})
    /// </code>
    /// </summary>
    public InsertBuilder<T> SetEntity(params T[] entities)
    {
        Object = entities;
        Type = entities.IsNullOrEmpty() ? EnumInsertBuilderType.InsertByDto : EnumInsertBuilderType.InsertByEntity;
        return this;
    }
    /// <summary>
    /// 设置实体, 如:
    /// <code>
    /// db.Insert&lt;PersonEntity>().SetEntity([new PersonEntity{...}])
    /// </code>
    /// </summary>
    public InsertBuilder<T> SetEntity(IEnumerable<T> entities)
    {
        Object = entities;
        Type = entities.IsNullOrEmpty() ? EnumInsertBuilderType.InsertByDto : EnumInsertBuilderType.InsertByEntity;
        return this;
    }
    #endregion
    #region 复写 BatchOption
    public override InsertBuilder<T> BatchOption(int? rowsCountPerBatch, IsolationLevel? batchIsolationLevel = null)
        => base.BatchOption(rowsCountPerBatch, batchIsolationLevel) as InsertBuilder<T>;
    #endregion
    #region 复写AsTable
    public override InsertBuilder<T> AsTable(string tableName)
    {
        base.AsTable(tableName);
        return this;
    }
    public override InsertBuilder<T> AsTableIf(bool condition, string tableName)
        => condition ? AsTable(tableName) : this;
    public override InsertBuilder<T> AsTable(Func<string, string> func)
        => base.AsTable(func) as InsertBuilder<T>;
    public override InsertBuilder<T> AsTableIf(bool condition, Func<string, string> func)
        => base.AsTableIf(condition, func) as InsertBuilder<T>;
    public override InsertBuilder<T> AsTable(Action<TableName> func)
        => AsTable(func);
    public override InsertBuilder<T> AsTableIf(bool condition, Action<TableName> func)
        => AsTableIf(condition, func);
    #endregion
    #region 复写超时设置
    public override InsertBuilder<T> CommandTimeout(int timeoutSeconds) => base.CommandTimeout(timeoutSeconds) as InsertBuilder<T>;
    public override InsertBuilder<T> CommandTimeoutIf(bool condition, int timeoutSeconds)
        => condition ? CommandTimeout(timeoutSeconds) : this;
    #endregion

    #region InsertIdentity
    private bool _isInsertIdentity = false;
    /// <summary>
    /// 设置将自增主键当做普通列插入
    /// </summary>
    /// <remarks>
    /// 注: 一般自增主键是不参与插入, 如: 
    /// <code>
    /// [Table("t_person")]
    /// public class PersonEntity
    /// { 
    ///     [PrimaryKey,Identity]
    ///     prop int Id;
    ///     prop string Name;
    /// }
    /// 
    /// var insert = db.Insert&lt;PersonEntity>().SetEntity(new PersonEntity{Id=2,Name="jack"});
    /// insert.ToSql();
    /// //输出: insert into t_person(Name) values('jack');
    /// 
    /// insert.InsertIdentity().ToSql();
    /// //输出: insert into t_person(Id,Name) values(2,'jack');
    /// </code>
    /// </remarks>
    public InsertBuilder<T> InsertIdentity() => SwitchInsertIdentity(true);
    /// <summary>
    /// 切换是否插入自增主键, <seealso cref="InsertIdentity"/>
    /// </summary>
    public InsertBuilder<T> SwitchInsertIdentity(bool isEnable)
    {
        _isInsertIdentity = isEnable;
        return this;
    }
    #endregion
    #region SetColumn & IgnoreColumns
    private string[] ConvertColumnPureNames(params string[] names)
    {
        var arr = names.Select(name => EntityInfo.Props.FirstOrDefault(col => string.Equals(col.ColumnNamePure, name, StringComparison.OrdinalIgnoreCase) || string.Equals(col.ColumnNameQuoted, name))).ToList();
        var err = string.Empty;
        for (var i = 0; i < arr.Count; i++)
        {
            if (arr[i] == null) err += (err.Length > 0 ? "," : "") + $"【{names[i]}】";
        }
        if (err.Length != 0) throw new Exception($"类({typeof(T).GetClassFullName()})上不存在列({err})!");
        return arr.Select(i => i.ColumnNamePure).ToArray();
    }
    internal new InsertBuilder<T> ReplaceSetIgnores(List<SetIgnoreItem> newSetIgnores) => base.ReplaceSetIgnores(newSetIgnores) as InsertBuilder<T>;
    public override InsertBuilder<T> SetColumn(string colname, object constantVal)
    {
        base.SetColumn(ConvertColumnPureNames(colname).FirstOrDefault(), constantVal);
        return this;
    }
    public override InsertBuilder<T> SetColumnIf(bool condition, string colname, object constantVal) => condition ? SetColumn(colname, constantVal) : this;
    public override InsertBuilder<T> IgnoreColumns(params string[] colNames)
    {
        base.IgnoreColumns(ConvertColumnPureNames(colNames));
        return this;
    }
    public override InsertBuilder<T> IgnoreColumnsIf(bool condition, params string[] colNames) => condition ? IgnoreColumns(colNames) : this;
    public override InsertBuilder<T> OnlyColumns(params string[] colNames)
    {
        base.OnlyColumns(ConvertColumnPureNames(colNames));
        return this;
    }
    public override InsertBuilder<T> OnlyColumnsIf(bool condition, params string[] colNames) => condition ? OnlyColumns(colNames) : this;

    /// <summary>
    /// 示例: 
    /// <list type="bullet">
    /// <item>insert.SetColumn(i=>i.Name, "jack")</item>
    /// </list>
    /// </summary>
    public virtual InsertBuilder<T> SetColumn<TValue>(Expression<Func<T, TValue>> propSelector, TValue constantVal)
    {
        var names = SetIgnoreItem.GetDepthPropNames(propSelector);
        if (names.IsNullOrEmpty()) throw new Exception($"格式错误,无法获取指定的列: {propSelector}!");

        SetIgnoreItem.SetColumn(setIgnores, EntityInfo, names, constantVal);
        return this;
    }
    /// <summary>
    /// 示例: 
    /// <list type="bullet">
    /// <item>insert.SetColumnIf(true, i=>i.Name, "jack")</item>
    /// </list>
    /// </summary>
    public virtual InsertBuilder<T> SetColumnIf<TValue>(bool condition, Expression<Func<T, TValue>> propSelector, TValue constantVal)
        => condition ? SetColumn(propSelector, constantVal) : this;
    /// <summary>
    /// 示例: 
    /// <list type="bullet">
    /// <item>insert.IgnoreColumns(i=>i.Name)</item>
    /// <item>insert.IgnoreColumns(i=>new{ i.Name, i.Name2})</item>
    /// </list>
    /// </summary>
    /// <remarks>
    /// 注意: 下面的形式不支持
    /// <list type="bullet">
    /// <item>insert.IgnoreColumns(i=>i.Ext.Detail.Name)</item>
    /// </list>
    /// </remarks>
    public virtual InsertBuilder<T> IgnoreColumns(Expression<Func<T, object>> propSelector)
    {
        SetIgnoreItem.IgnoreColumns(setIgnores, EntityInfo, propSelector);
        return this;
    }
    /// <summary>
    /// 示例: 
    /// <list type="bullet">
    /// <item>insert.IgnoreColumnsIf(true, i=>i.Name)</item>
    /// <item>insert.IgnoreColumnsIf(1>0, i=>new{ i.Name, i.Name2})</item>
    /// </list>
    /// </summary>
    /// <remarks>
    /// 注意: 下面的形式不支持
    /// <list type="bullet">
    /// <item>insert.IgnoreColumnsIf(true,i=>i.Ext.Detail.Name)</item>
    /// </list>
    /// </remarks>
    public virtual InsertBuilder<T> IgnoreColumnsIf(bool condition, Expression<Func<T, object>> propSelector)
        => condition ? IgnoreColumns(propSelector) : this;
    /// <summary>
    /// 示例: 
    /// <list type="bullet">
    /// <item>insert.OnlyColumns(i=>i.Name)</item>
    /// <item>insert.OnlyColumns(i=>new{ i.Name, i.Name2})</item>
    /// </list>
    /// </summary>
    /// <remarks>
    /// 注意: 下面的形式不支持
    /// <list type="bullet">
    /// <item>insert.OnlyColumns(i=>i.Ext.Detail.Name)</item>
    /// </list>
    /// </remarks>
    public virtual InsertBuilder<T> OnlyColumns(Expression<Func<T, object>> propSelector)
    {
        SetIgnoreItem.OnlyColumns(setIgnores, EntityInfo, propSelector);
        return this;
    }
    /// <summary>
    /// 示例: 
    /// <list type="bullet">
    /// <item>insert.OnlyColumnsIf(true, i=>i.Name)</item>
    /// <item>insert.OnlyColumnsIf(1>0, i=>new{ i.Name, i.Name2})</item>
    /// </list>
    /// </summary>
    /// <remarks>
    /// 注意: 下面的形式不支持
    /// <list type="bullet">
    /// <item>insert.OnlyColumnsIf(i=>i.Ext.Detail.Name)</item>
    /// </list>
    /// </remarks>
    public virtual InsertBuilder<T> OnlyColumnsIf(bool condition, Expression<Func<T, object>> propSelector)
        => condition ? OnlyColumns(propSelector) : this;
    #endregion

    #region 执行
    /// <summary>
    /// 插入并返回实体
    /// </summary>
    public virtual T ExecuteInserted()
    {
        //todo? 插入10条数据,只返回一条? 不考虑这种情况吧
        var sqls = ToSqlMulti(EnumInsertToSql.ExecuteInserted);
        if (sqls.Count > 1)
        {
            return db.RunInTransaction(() =>
            {
                for (int i = 0; i < sqls.Count - 1; i++)
                {
                    RunWriteMonitor(new AfterWriteArgument
                    {
                        EntityInfo = EntityInfo,
                        TableName = TableName.FullNameQuoted,
                        WriteType = EnumWriteType.Insert
                    }, () => db.ExecuteSql(sqls[i], CommandType.Text, TimeoutSeconds));
                }
                return RunWriteMonitor(new AfterWriteArgument
                {
                    EntityInfo = EntityInfo,
                    TableName = TableName.FullNameQuoted,
                    WriteType = EnumWriteType.Insert
                }, () => db.SelectModel<T>(sqls[^1], CommandType.Text, TimeoutSeconds));
            }, batchIsolationLevel);
        }
        else
        {
            return RunWriteMonitor(new AfterWriteArgument
            {
                EntityInfo = EntityInfo,
                TableName = TableName.FullNameQuoted,
                WriteType = EnumWriteType.Insert
            }, () => db.SelectModel<T>(sqls[0], CommandType.Text, TimeoutSeconds));
        }
    }

    /// <summary>
    /// 插入并返回实体
    /// </summary>
    public virtual async Task<T> ExecuteInsertedAsync(CancellationToken cancellationToken = default)
    {
        //todo? 插入10条数据,只返回一条? 不考虑这种情况吧
        var sqls = ToSqlMulti(EnumInsertToSql.ExecuteInserted);
        if (sqls.Count > 1)
        {
            return await db.RunInTransactionAsync(async () =>
            {
                for (int i = 0; i < sqls.Count - 1; i++)
                {
                    await RunWriteMonitorAsync(new AfterWriteArgument
                    {
                        EntityInfo = EntityInfo,
                        TableName = TableName.FullNameQuoted,
                        WriteType = EnumWriteType.Insert
                    }, async () => await db.ExecuteSqlAsync(sqls[i], CommandType.Text, TimeoutSeconds, null, cancellationToken));
                }
                return await RunWriteMonitorAsync(new AfterWriteArgument
                {
                    EntityInfo = EntityInfo,
                    TableName = TableName.FullNameQuoted,
                    WriteType = EnumWriteType.Insert
                }, async () => await db.SelectModelAsync<T>(sqls[^1], CommandType.Text, TimeoutSeconds, null, cancellationToken));
            }, batchIsolationLevel);
        }
        else
        {
            return await RunWriteMonitorAsync(new AfterWriteArgument
            {
                EntityInfo = EntityInfo,
                TableName = TableName.FullNameQuoted,
                WriteType = EnumWriteType.Insert
            }, async () => await db.SelectModelAsync<T>(sqls[0], CommandType.Text, TimeoutSeconds, null, cancellationToken));
        }
    }

    /// <summary>
    /// 插入并返回实体集合
    /// </summary>
    public virtual List<T> ExecuteInsertedList()
    {
        var sqls = ToSqlMulti(EnumInsertToSql.ExecuteInserted);
        if (sqls.Count > 1)
        {
            return db.RunInTransaction(() =>
            {
                for (int i = 0; i < sqls.Count - 1; i++)
                {
                    RunWriteMonitor(new AfterWriteArgument
                    {
                        EntityInfo = EntityInfo,
                        TableName = TableName.FullNameQuoted,
                        WriteType = EnumWriteType.Insert
                    }, () => db.ExecuteSql(sqls[i], CommandType.Text, TimeoutSeconds));
                }
                return RunWriteMonitor(new AfterWriteArgument
                {
                    EntityInfo = EntityInfo,
                    TableName = TableName.FullNameQuoted,
                    WriteType = EnumWriteType.Insert
                }, () => db.SelectModelList<T>(sqls[^1], CommandType.Text, TimeoutSeconds));
            }, batchIsolationLevel);
        }
        else
        {
            return RunWriteMonitor(new AfterWriteArgument
            {
                EntityInfo = EntityInfo,
                TableName = TableName.FullNameQuoted,
                WriteType = EnumWriteType.Insert
            }, () => db.SelectModelList<T>(sqls[0], CommandType.Text, TimeoutSeconds));
        }
    }
    /// <summary>
    /// 插入并返回实体集合
    /// </summary>
    /// <returns></returns>
    public virtual async Task<List<T>> ExecuteInsertedListAsync(CancellationToken cancellationToken = default)
    {
        var sqls = ToSqlMulti(EnumInsertToSql.ExecuteInserted);
        if (sqls.Count > 1)
        {
            return await db.RunInTransactionAsync(async () =>
            {
                for (int i = 0; i < sqls.Count - 1; i++)
                {
                    await RunWriteMonitorAsync(new AfterWriteArgument
                    {
                        EntityInfo = EntityInfo,
                        TableName = TableName.FullNameQuoted,
                        WriteType = EnumWriteType.Insert
                    }, async () => await db.ExecuteSqlAsync(sqls[i], CommandType.Text, TimeoutSeconds, null, cancellationToken));
                }
                return await RunWriteMonitorAsync(new AfterWriteArgument
                {
                    EntityInfo = EntityInfo,
                    TableName = TableName.FullNameQuoted,
                    WriteType = EnumWriteType.Insert
                }, async () => await db.SelectModelListAsync<T>(sqls[^1], CommandType.Text, TimeoutSeconds, null, cancellationToken));
            }, batchIsolationLevel);
        }
        else
        {
            return await RunWriteMonitorAsync(new AfterWriteArgument
            {
                EntityInfo = EntityInfo,
                TableName = TableName.FullNameQuoted,
                WriteType = EnumWriteType.Insert
            }, async () => await db.SelectModelListAsync<T>(sqls[0], CommandType.Text, TimeoutSeconds, null, cancellationToken));
        }
    }
    #endregion

    #region ToDataTable
    /// <summary>
    /// 便于 BulkCopy
    /// </summary>
    public override DataTable ToDataTable()
    {
        if (Type == EnumInsertBuilderType.InsertByDictionary) return base.ToDataTable();
        if (Type == EnumInsertBuilderType.InsertByDto) return ToDataTableByDto();
        if (Type == EnumInsertBuilderType.InsertByEntity) return ToDataTableByEntity();
        throw new Exception($"错误的类型:{Type}!");
    }

    private DataTable ToDataTableByDto()
    {
        var dt = new DataTable { TableName = TableName.Name };

        var allProps = EntityInfo.Props.Where(i => i.CanMapToTable && !i.IsIgnoreInsert).ToList();
        var setList = new List<SetListItem>();//set列

        //进行 setIgnore 过滤
        BuilderHelper.ApplySetIgnore(setList, setIgnores, allProps);

        //先把json列提出来
        var jsonSetList = BuilderHelper.SplitJsonStore(setList);
        var groups = jsonSetList.GroupBy(i => i.PropertyInfo.ColumnNamePure).ToList();

        //先设置列头
        var colType = typeof(object);
        foreach (var setItem in setList) dt.Columns.Add(setItem.ColumnNamePure, colType);
        foreach (var setItem in groups) dt.Columns.Add(setItem.Key, colType);

        var row = dt.NewRow();
        dt.Rows.Add(row);
        //就一行
        //先处理非json列
        for (int i = 0; i < setList.Count; i++)
        {
            var setItem = setList[i];
            var col = setItem.ColumnNamePure;
            var val = setItem.Value;
            var enum2String = setItem.PropertyInfo?.IsEnum == true && setItem.PropertyInfo?.IsDbString == true;
            var activeTimeFormat = setItem.PropertyInfo?.ActiveTimeFormat == true;
            if (enum2String && val?.GetType().IsEnum == true)
            {
                val = val.ToString();
            }
            else if (activeTimeFormat)
            {
                //activeTimeFormat 为 true 那么 setItem.PropertyInfo.TimeFormat 比有值
                val = val.To<string>(setItem.PropertyInfo.TimeFormat);
            }
            row[col] = val;
        }

        //再处理json列
        if (groups.IsNotNullOrEmpty())
        {
            for (int i = 0; i < groups.Count; i++)
            {
                var g = groups[i];
                var bucket = g.Key;
                var colList = g.ToList();
                //先将sql执行了
                foreach (var item in colList)
                {
                    if (item.FromSet && item.Value is RawString raw)
                    {
                        item.Value = db.SelectScalar<object>(raw.String);
                    }
                }

                var val = BuildJsonNode(colList.Select(i => (i.JsonDepthProps, i.Value)).ToList());
                row[bucket] = val.ToJsonFast(ignoreNull: true);
            }
        }
        return dt;
    }
    private DataTable ToDataTableByEntity()
    {
        var dt = new DataTable { TableName = TableName.Name };

        var allProps = EntityInfo.Props.Where(i => i.CanMapToTable && !i.IsIgnoreInsert).ToList();
        var setList = new List<SetListItem>();//set列
        foreach (var prop in allProps)
            if (!prop.IsPrimaryKey || prop.PrimaryKeyStrategy != EnumKeyStrategy.Identity || _isInsertIdentity)
                setList.Add(new SetListItem(prop, prop.ColumnNamePure, null, false, null));
        var accessor = Accessor.Build<T>();

        //进行 setIgnore 过滤
        BuilderHelper.ApplySetIgnore(setList, setIgnores, allProps);

        //先转换成实体数组
        T[] arr = null;
        if (Object is IEnumerable<T> tmp) arr = tmp.ToArray();
        else if (Object is T tmpT) arr = [tmpT];
        else throw new Exception("InsertByEntity 参数错误!");

        //先把json列提出来
        var jsonSetList = BuilderHelper.SplitJsonStore(setList);
        var groups = jsonSetList.GroupBy(i => i.PropertyInfo.ColumnNamePure).ToList();

        //先设置列头
        var colType = typeof(object);
        foreach (var setItem in setList) dt.Columns.Add(setItem.ColumnNamePure, colType);
        foreach (var setItem in groups) dt.Columns.Add(setItem.Key, colType);

        //行数
        var len = arr.Count();
        for (int i = 0; i < len; i++)
        {
            var row = dt.NewRow();
            dt.Rows.Add(row);
        }

        //先处理非json列
        for (int i = 0; i < setList.Count; i++)
        {
            var setItem = setList[i];
            var col = setItem.ColumnNamePure;
            for (int j = 0; j < len; j++)
            {
                var ent = arr[j];
                var row = dt.Rows[j];
                object val = null;
                if (setItem.FromSet)
                {
                    //来自外部set
                    val = setItem.Value;
                }
                else
                {
                    //从实体属性读取值
                    val = accessor[ent, setItem.PropertyInfo.PropNamePure];
                }
                var enum2String = setItem.PropertyInfo?.IsEnum == true && setItem.PropertyInfo?.IsDbString == true;
                var activeTimeFormat = setItem.PropertyInfo?.ActiveTimeFormat == true;
                if (enum2String && val?.GetType().IsEnum == true)
                {
                    val = val.ToString();
                }
                else if (activeTimeFormat)
                {
                    //activeTimeFormat 为 true 那么 setItem.PropertyInfo.TimeFormat 比有值
                    val = val.To<string>(setItem.PropertyInfo.TimeFormat);
                }
                row[col] = val;
            }
        }

        //再处理json列
        if (groups.IsNotNullOrEmpty())
        {
            for (int i = 0; i < groups.Count; i++)
            {
                var g = groups[i];
                var bucket = g.Key;
                var colList = g.ToList();
                //先将sql执行了
                foreach (var item in colList)
                {
                    if (item.FromSet && item.Value is RawString raw)
                    {
                        item.Value = db.SelectScalar<object>(raw.String);
                    }
                }

                for (int j = 0; j < len; j++)
                {
                    var ent = arr[j];
                    var row = dt.Rows[j];
                    var val = BuildJsonNode(colList.Select(i => (i.JsonDepthProps, i.FromSet ? i.Value : accessor[ent, i.PropertyInfo.PropNamePure])).ToList());
                    row[bucket] = val.ToJsonFast(ignoreNull: true);
                }
            }
        }
        return dt;
    }
    #endregion

    #region ToSql
    /// <summary>
    /// 返回生成的sql
    /// </summary>
    public override string ToSql(EnumInsertToSql enumInsertToSql)
    {
        if (Type == EnumInsertBuilderType.InsertByDictionary) return base.ToSql(enumInsertToSql);
        if (Type == EnumInsertBuilderType.InsertByDto) return ToSqlByDto(enumInsertToSql);
        if (Type == EnumInsertBuilderType.InsertByEntity) return ToSqlByEntity(enumInsertToSql, false).First();
        throw new Exception($"错误的类型:{Type}!");
    }
    public override string ToSql() => ToSql(EnumInsertToSql.ExecuteAffrows);
    public override List<string> ToSqlMulti() => ToSqlMulti(EnumInsertToSql.ExecuteAffrows);
    public override List<string> ToSqlMulti(EnumInsertToSql enumInsertToSql)
    {
        if (Type == EnumInsertBuilderType.InsertByDictionary) return [base.ToSql(enumInsertToSql)];
        if (Type == EnumInsertBuilderType.InsertByDto) return [ToSqlByDto(enumInsertToSql)];
        if (Type == EnumInsertBuilderType.InsertByEntity) return ToSqlByEntity(enumInsertToSql, true);
        throw new Exception($"错误的类型:{Type}!");
    }

    private string ToSqlByDto(EnumInsertToSql enumInsertToSql)
    {
        var (primary, primaryMulti) = Validate(enumInsertToSql);
        var allProps = EntityInfo.Props.Where(i => i.CanMapToTable && !i.IsIgnoreInsert).ToList();
        var setList = new List<SetListItem>();//set列

        //进行 setIgnore 过滤
        BuilderHelper.ApplySetIgnore(setList, setIgnores, allProps);

        //先把json列提出来
        var jsonSetList = BuilderHelper.SplitJsonStore(setList);

        //
        var sbHeader = new StringBuilder();
        var sbRow = new StringBuilder();
        sbHeader.Append($"insert into ").Append(TableName.FullNameQuoted).Append('(');
        sbRow.Append(" values(");

        //缓存的所有行主键值 单列主键[seg...]
        var priValue = string.Empty;
        //缓存的所有行主键值 联合主键 [{col1:seg1}...]
        var priMultiValue = new Dictionary<string, string>();
        var index = 0;//进行的列
        //先处理非json列
        for (int i = 0; i < setList.Count; i++)
        {
            var setItem = setList[i];
            var colPure = setItem.ColumnNamePure;
            var colQuoted = setItem.PropertyInfo?.ColumnNameQuoted ?? db.AddQuote(colPure);
            if (index > 0)
            {
                sbHeader.Append(',');
                sbRow.Append(',');
            }
            sbHeader.Append(colQuoted);

            //c#枚举,db中为 string 需要特殊处理
            var enum2String = setItem.PropertyInfo?.IsEnum == true && setItem.PropertyInfo?.IsDbString == true;
            var activeTimeFormat = setItem.PropertyInfo?.ActiveTimeFormat == true;

            //来自外部set
            var seg = db.ConvertToSqlSeg(setItem.Value, enum2String ? true : activeTimeFormat ? setItem.PropertyInfo.TimeFormat : null).UnWrap();

            sbRow.Append(seg);

            if (enumInsertToSql == EnumInsertToSql.ExecuteInserted && setItem.PropertyInfo != null)
            {
                if (primary != null && primary.ColumnNamePure == colPure) priValue = seg;
                if (primaryMulti.Count > 1 && primaryMulti.Any(i => i.ColumnNameQuoted == colPure))
                    priMultiValue[colQuoted] = seg;
            }
            index++;
        }
        //再处理json列
        if (jsonSetList.IsNotNullOrEmpty())
        {
            var groups = jsonSetList.GroupBy(i => i.PropertyInfo.ColumnNamePure).ToList();
            for (int i = 0; i < groups.Count; i++)
            {
                var g = groups[i];
                var bucket = g.Key;
                var colList = g.ToList();
                var bucketQuoted = colList.FirstOrDefault(i => i.PropertyInfo?.ColumnNameQuoted != null)?.PropertyInfo?.ColumnNameQuoted ?? db.AddQuote(bucket);
                //不能处理 Json 深度赋值时 RawString
                foreach (var item in colList)
                {
                    if (item.FromSet && item.JsonDepthProps.IsNotNullOrEmpty() && item.Value is RawString raw)
                    {
                        throw new Exception($"无法处理类似【SetColumn(i=>i.Ext.Detail.Count, new RawString(db.Select<Person>().ToSqlCount())】的设置, 请避免给 Json 深度赋值时使用 RawString 格式!");
                    }
                }

                if (index > 0)
                {
                    sbHeader.Append(',');
                    sbRow.Append(',');
                }
                sbHeader.Append(bucketQuoted);

                object val = null;
                val = BuildJsonNode(colList.Select(i => (i.JsonDepthProps, i.Value)).ToList());
                val = db.ConvertJsonLiteralToSql(val, EnumJsonAcceptAsType.Doc).res;
                sbRow.Append(val);
                index++;
            }
        }
        sbHeader.Append(')');
        sbRow.Append(");");

        var sbTail = new StringBuilder();
        if (enumInsertToSql == EnumInsertToSql.ExecuteIdentity)
        {
            //返回自增的主键值 查询后再处理个数
            if (primary.PrimaryKeyStrategy != EnumKeyStrategy.Identity || (primary.PrimaryKeyStrategy == EnumKeyStrategy.Identity && _isInsertIdentity)) throw new Exception("非自增主键或已声明插入自增列,无法获取自动生成的主键值!");
            sbTail.Append("\r\n").Append($"select {db.GetLastInsertedIdSeg()};");
        }
        else if (enumInsertToSql == EnumInsertToSql.ExecuteInserted)
        {
            //segs: "id `Id`,name `Name`"
            var segs = EntityInfo.Props.Where(i => !i.IsIgnoreSelect).Select(i => i.SelectFunc(null)).ToStringSeparated(",");

            if (primary.PrimaryKeyStrategy == EnumKeyStrategy.Identity && !_isInsertIdentity)
            {
                //自动生成的主键
                sbTail.Append($"\r\nselect {segs} from {TableName.FullNameQuoted} where {primary.ColumnNameQuoted}={db.GetLastInsertedIdSeg()};");
            }
            else
            {
                //普通或插入的自增主键
                if (priMultiValue.Count > 1)
                {
                    //联合主键
                    //(id,id2) = (1,2)
                    var keys = priMultiValue.Keys.OrderBy(i => i).ToList();
                    var col = "(" + keys.ToStringSeparated(",") + ")";
                    var vals = "(" + priMultiValue.ToList().OrderBy(i => i.Key).Select(i => i.Value).ToStringSeparated(",") + ")";
                    var filter = $"{col} = {vals}";
                    sbTail.Append($"\r\nselect {segs} from {TableName.FullNameQuoted} where {filter};");
                }
                else
                {
                    //单个主键
                    sbTail.Append($"\r\nselect {segs} from {TableName.FullNameQuoted} where {primary.ColumnNameQuoted} = {priValue};");
                }
            }
        }
        //合并 sbRow 到 sbHeader
        sbHeader.Append(sbRow);
        sbHeader.Append(sbTail);
        var sql = sbHeader.ToString();
        //清空
        sbHeader.Clear(); sbRow.Clear(); sbTail.Clear();
        return sql;
    }

    #region BuildJsonNode/BuildEmptyJsonObject/BuildDeepJsonObject
    /// <summary>
    /// 传入:
    /// <code>
    /// [
    ///     (["Ext","Detail","Name"],"jack"),
    ///     (["Ext","Detail","Id"],10),
    /// ]
    /// </code>
    /// 得到:
    /// <code>
    /// {
    ///     "Ext":{
    ///         "Detail":{
    ///             "Name":"jack",
    ///             "Id":10,
    ///         }
    ///     }
    /// }
    /// </code>
    /// </summary>
    private JsonNode BuildJsonNode(List<(List<string> JsonDepthProps, object Value)> setList)
    {
        JsonNode root = null;
        for (int i = 0; i < setList.Count; i++)
        {
            var set = setList[i];
            if (set.JsonDepthProps.IsNullOrEmpty())
            {
                root = ToJsonNode(set.Value);
            }
            else
            {
                BuildDeepJsonObject(set.JsonDepthProps, set.Value, ref root);
            }
        }
        return root;
    }
    /// <summary>
    /// 将常量转为 JsonNode 形式, 注意: 已经是 JsonNode 则返回 DeepClone 的副本, 否则转换为 JsonNode.
    /// </summary>
    private JsonNode ToJsonNode(object obj)
    {
        if (obj is null) return null;
        if (obj is JsonNode node) return node.DeepClone();
        var json = obj.ToJsonFast(ignoreNull: true, ignoreJsonPropertyName: true);
        return JsonNode.Parse(json);
    }

    /// <summary>
    /// 传入:
    /// <code>
    /// ["Ext","Detail","Name"]
    /// </code>
    /// 传出:
    /// <code>
    /// {
    ///     "Ext":{
    ///         "Detail":{}
    ///     }
    /// }
    /// </code>
    /// </summary>
    private JsonObject BuildEmptyJsonObject(List<string> props)
    {
        var obj = new JsonObject();
        var botObj = obj;
        for (int i = 0; i < props.Count - 1; i++)
        {
            botObj[props[i]] = new JsonObject();
            botObj = botObj[props[i]].AsObject();
        }
        return obj;
    }

    /// <summary>
    /// 传入:
    /// <code>
    /// props: ["Ext","Detail","Name"]
    /// constObj: "jack"
    /// root: 参照根节点, 假设为null
    /// </code>
    /// 处理后, root为:
    /// <code>
    /// {
    ///     "Ext":{
    ///         "Detail":{
    ///             "Name":"jack"
    ///         }
    ///     }
    /// }
    /// </code>
    /// </summary>
    private void BuildDeepJsonObject(List<string> props, object constObj, ref JsonNode root)
    {
        root ??= new JsonObject();
        var botObj = root;
        for (int i = 0; i < props.Count; i++)
        {
            if (i == props.Count - 1)
            {
                botObj[props[i]] = ToJsonNode(constObj);
            }
            else
            {
                if (!botObj.AsObject().ContainsKey(props[i])) botObj[props[i]] = new JsonObject();
                botObj = botObj[props[i]].AsObject();
            }
        }
    }
    #endregion

    private List<string> ToSqlByEntity(EnumInsertToSql enumInsertToSql, bool multi)
    {
        var (primary, primaryMulti) = Validate(enumInsertToSql);
        var allProps = EntityInfo.Props.Where(i => i.CanMapToTable && !i.IsIgnoreInsert).ToList();
        var setList = new List<SetListItem>();//set列
        foreach (var prop in allProps)
            if (!prop.IsPrimaryKey || prop.PrimaryKeyStrategy != EnumKeyStrategy.Identity || _isInsertIdentity)
                setList.Add(new SetListItem(prop, prop.ColumnNamePure, null, false, null));
        var accessor = Accessor.Build<T>();

        //进行 setIgnore 过滤
        BuilderHelper.ApplySetIgnore(setList, setIgnores, allProps);

        //先转换成实体数组
        T[] arr = null;
        if (Object is IEnumerable<T> tmp) arr = tmp.ToArray();
        else if (Object is T tmpT) arr = [tmpT];
        else throw new Exception("InsertByEntity 参数错误!");
        if (arr.Length == 0) throw new Exception($"InsertByEntity 要求指定至少一行数据!");

        //先把json列提出来
        var jsonSetList = BuilderHelper.SplitJsonStore(setList);
        var groups = jsonSetList.GroupBy(i => i.PropertyInfo.ColumnNamePure).ToList();

        //行数
        var len = arr.Length;
        //insert into test(id,name)
        var sbHeader = new StringBuilder();
        //分批次
        var rowsCountPerBatch = multi == false ? int.MaxValue : (this.rowsCountPerBatch ?? db.InsertRowsMaxCountPerBatch);
        var batchCount = len / rowsCountPerBatch + (len % rowsCountPerBatch > 0 ? 1 : 0);
        //每一个 sbBatch: (1,'jack'),\r\n(2,'tom');
        var sbBatchs = new StringBuilder[batchCount];
        for (int i = 0; i < sbBatchs.Length; i++) sbBatchs[i] = new StringBuilder();

        sbHeader.Append($"insert into ").Append(TableName.FullNameQuoted).Append('(');
        //缓存的所有行主键值 单列主键[seg...]
        var priValue = new List<string>();
        //缓存的所有行主键值 联合主键 [{col1:seg1}...]
        var priMultiValue = new Dictionary<string, object>[len];
        for (int i = 0; i < len; i++) priMultiValue[i] = [];

        for (int r = 0; r < len; r++)//每一行
        {
            //先做 batch 计算
            //假设 len=11 rowsCountPerBatch=5
            //即: 0 1 2 3 4      5 6 7 8 9       10
            //那么 r=6 时,  batchIndex=1; sbBatch 是第二个 batch; 当前batch有 5 行数据
            //那么 r=10 时, batchIndex=2; sbBatch 是第三个 batch; 当前batch有 1 行数据
            var batchIndex = r / rowsCountPerBatch;//第几个batch
            var sbBatch = sbBatchs[batchIndex];//当前所属batch
            var isBatchFirstRow = r % rowsCountPerBatch == 0;//是否是batch内第一行
            var isBatchSecondRow = r % rowsCountPerBatch == 1;//是否是batch内第二行
            var isBatchEndRow = r % rowsCountPerBatch == rowsCountPerBatch - 1 || r == len - 1;//是否是batch内最后一行

            if (isBatchSecondRow)
            {
                //第二行的时候控制第一行换行缩进
                sbBatch.Insert(0, "\r\n    ");
                sbBatch.Append("\r\n    ");
            }
            else if (!isBatchFirstRow)
            {
                //不是第二行也不是第一行 自己换行缩进
                sbBatch.Append("\r\n    ");
            }
            sbBatch.Append('(');

            var ent = arr[r];//当前行实体
            var index = 0;//进行的列
            //先处理非json列
            for (int i = 0; i < setList.Count; i++)
            {
                var setItem = setList[i];
                var colPure = setItem.ColumnNamePure;
                var colQuoted = setItem.PropertyInfo?.ColumnNameQuoted ?? db.AddQuote(colPure);
                if (r == 0)
                {
                    //仅第一行需要处理header
                    if (index > 0) sbHeader.Append(',');
                    sbHeader.Append(colQuoted);
                }

                //非第一列, 需要加入逗号
                if (index > 0) sbBatch.Append(',');

                string seg = null;
                //c#枚举,db中为 string 需要特殊处理
                var enum2String = setItem.PropertyInfo?.IsEnum == true && setItem.PropertyInfo?.IsDbString == true;
                var activeTimeFormat = setItem.PropertyInfo?.ActiveTimeFormat == true;
                //其他
                if (setItem.FromSet)
                {
                    //来自外部set
                    seg = db.ConvertToSqlSeg(setItem.Value, enum2String ? true : activeTimeFormat ? setItem.PropertyInfo.TimeFormat : null).UnWrap();
                }
                else
                {
                    //从实体属性读取值
                    seg = db.ConvertToSqlSeg(accessor[ent, setItem.PropertyInfo.PropNamePure], enum2String ? true : activeTimeFormat ? setItem.PropertyInfo.TimeFormat : null).UnWrap();
                }
                sbBatch.Append(seg);

                //处理主键
                if (enumInsertToSql == EnumInsertToSql.ExecuteInserted && setItem.PropertyInfo != null)
                {
                    if (primary != null && primary.ColumnNamePure == colPure) priValue.Add(seg);
                    if (primaryMulti.Count > 1 && primaryMulti.Any(i => i.ColumnNamePure == colPure))
                        priMultiValue[r][colQuoted] = seg;
                }
                index++;
            }
            //再处理json列
            if (jsonSetList.IsNotNullOrEmpty())
            {
                for (int i = 0; i < groups.Count; i++)
                {
                    var g = groups[i];
                    var bucket = g.Key;
                    var colList = g.ToList();
                    var bucketQuoted = colList.FirstOrDefault(i => i.PropertyInfo?.ColumnNameQuoted != null)?.PropertyInfo?.ColumnNameQuoted ?? db.AddQuote(bucket);
                    if (r == 0)
                    {
                        //第一行校验: 不能处理 Json 深度赋值时 RawString
                        foreach (var item in colList)
                        {
                            if (item.FromSet && item.JsonDepthProps.IsNotNullOrEmpty() && item.Value is RawString raw)
                            {
                                throw new Exception($"无法处理类似【SetColumn(i=>i.Ext.Detail.Count, new RawString(db.Select<Person>().ToSqlCount())】的设置, 请避免给 Json 深度赋值时使用 RawString 格式!");
                            }
                        }
                    }

                    if (r == 0)
                    {
                        //仅第一行需要处理header
                        if (index > 0) sbHeader.Append(',');
                        sbHeader.Append(bucketQuoted);
                    }

                    //构建 jsonDoc
                    var jsonDoc = BuildJsonNode(colList.Select(i => (i.JsonDepthProps, i.FromSet ? i.Value : accessor[ent, i.PropertyInfo.PropNamePure])).ToList());
                    var jsonDocSqlSeg = db.ConvertJsonLiteralToSql(jsonDoc, EnumJsonAcceptAsType.Doc).res;
                    //非第一列, 需要加入逗号
                    if (index > 0) sbBatch.Append(',');
                    sbBatch.Append(jsonDocSqlSeg);
                    index++;
                }
            }

            //行结束
            sbBatch.Append(')');
            if (r == 0) sbHeader.Append(')');

            if (isBatchEndRow) sbBatch.Append(';');
            else sbBatch.Append(',');
        }

        var sbTail = new StringBuilder();
        if (enumInsertToSql == EnumInsertToSql.ExecuteIdentity)
        {
            //返回自增的主键值 查询后再处理个数
            if (primary.PrimaryKeyStrategy != EnumKeyStrategy.Identity || (primary.PrimaryKeyStrategy == EnumKeyStrategy.Identity && _isInsertIdentity)) throw new Exception("非自增主键或已声明插入自增列,无法获取自动生成的主键值!");
            sbTail.Append("\r\n").Append($"select {db.GetLastInsertedIdSeg(len)};");
        }
        else if (enumInsertToSql == EnumInsertToSql.ExecuteInserted)
        {
            //segs: "id `Id`,name `Name`"
            var segs = EntityInfo.Props.Where(i => !i.IsIgnoreSelect).Select(i => i.SelectFunc(null)).ToStringSeparated(",");

            if (primary.PrimaryKeyStrategy == EnumKeyStrategy.Identity && !_isInsertIdentity)
            {
                //自动生成的主键
                if (len > 1)
                {
                    sbTail.Append($"\r\nselect {segs} from {TableName.FullNameQuoted} where {primary.ColumnNameQuoted}>={db.GetLastInsertedIdSeg()} order by {primary.ColumnNameQuoted} limit {len};");
                }
                else
                {
                    sbTail.Append($"\r\nselect {segs} from {TableName.FullNameQuoted} where {primary.ColumnNameQuoted}={db.GetLastInsertedIdSeg()};");
                }
            }
            else
            {
                //普通或插入的自增主键
                if (priMultiValue.FirstOrDefault().Count > 1)
                {
                    //联合主键
                    //(id,id2) in ((1,2),(1,3))
                    var keys = priMultiValue.FirstOrDefault().Keys.OrderBy(i => i).ToList();
                    var col = "(" + keys.ToStringSeparated(",") + ")";
                    var vals = priMultiValue.Select(i =>
                     {
                         //return (1,2)
                         return "(" + i.ToList().OrderBy(i => i.Key).Select(i => i.Value).ToStringSeparated(",") + ")";
                     }).ToStringSeparated(",");
                    var filter = $"{col} in ({vals})";
                    sbTail.Append($"\r\nselect {segs} from {TableName.FullNameQuoted} where {filter};");
                }
                else
                {
                    //单个主键
                    if (len > 1)
                    {
                        sbTail.Append($"\r\nselect {segs} from {TableName.FullNameQuoted} where {primary.ColumnNameQuoted} in ({priValue.ToStringSeparated(",")});");
                    }
                    else
                    {
                        sbTail.Append($"\r\nselect {segs} from {TableName.FullNameQuoted} where {primary.ColumnNameQuoted} = {priValue.First()};");
                    }
                }
            }
        }
        //合并 sbRows 到 sbHeader
        if (sbBatchs.Length > 1)
        {
            //分批次
            var sqls = new List<string>(sbBatchs.Length);
            for (var i = 0; i < sbBatchs.Length; i++)
            {
                var sbBatch = sbBatchs[i];
                sbBatch.Insert(0, " values").Insert(0, sbHeader);
                if (i == sbBatchs.Length - 1) sbBatch.Append(sbTail);
                sqls.Add(sbBatch.ToString());
                sbBatch.Clear();
            }
            sbHeader.Clear(); sbTail.Clear();
            return sqls;
        }
        else
        {
            sbHeader.Append(" values");
            sbHeader.Append(sbBatchs[0]);
            sbHeader.Append(sbTail);
            var sql = sbHeader.ToString();
            sbHeader.Clear(); sbTail.Clear(); sbBatchs[0].Clear();
            return [sql];
        }
    }

    /// <summary>
    /// 基础校验
    /// </summary>
    private (EntityPropertyInfo primary, List<EntityPropertyInfo> primaryMulti) Validate(EnumInsertToSql enumInsertToSql)
    {
        var primary = EntityInfo.PrimaryKeyColumn;
        var primaryMulti = EntityInfo.Props.Where(i => i.IsPrimaryKey).OrderBy(i => i.Order).ToList();
        //基础校验
        if (primary == null && enumInsertToSql == EnumInsertToSql.ExecuteIdentity)
            throw new Exception($"当前Entity({EntityInfo.TypeClassFullName})未定义主键,无法获取插入后的自增主键值!");
        if (primaryMulti.Count > 1 && enumInsertToSql == EnumInsertToSql.ExecuteIdentity)
            throw new Exception($"当前Entity({EntityInfo.TypeClassFullName})使用联合主键({primaryMulti.Select(i => i.ColumnNameQuoted).ToStringSeparated(",")}),无法获取插入后的自增主键值!");
        if (primary == null && (enumInsertToSql == EnumInsertToSql.ExecuteInserted))
            throw new Exception($"当前Entity({EntityInfo.TypeClassFullName})未定义主键,无法查询插入后的数据!");
        return (primary, primaryMulti);
    }
    #endregion
}

/// <summary>
/// 插入构造器(从其他查询结果中插入)
/// </summary>
public class InsertFromSelectBuilder<T> : BaseBuilder where T : class, new()
{
    private readonly SelectBuilderBase selectBuilder;
    private readonly LambdaExpression expression;
    private readonly string[] propNames;
    private EntityInfo EntityInfo { get; set; }

    internal InsertFromSelectBuilder(DBAccess db, SelectBuilderBase selectBuilder, LambdaExpression expression, string[] propNames) : base(db)
    {
        this.EntityInfo = db.GetEntityInfoInternal<T>();
        this.selectBuilder = selectBuilder;
        this.expression = expression;
        this.propNames = propNames;
        this.TableName = this.EntityInfo.TableName.CloneTableName();
    }
    public TableName TableName { get; private set; }
    #region 复写AsTable
    public InsertFromSelectBuilder<T> AsTable(string newTableName)
    {
        TableName = db.ParseQuotedName(newTableName);
        return this;
    }
    public InsertFromSelectBuilder<T> AsTableIf(bool condition, string newTableName)
        => condition ? AsTable(newTableName) : this;
    public InsertFromSelectBuilder<T> AsTable(Func<string, string> func)
    {
        var newTableName = func?.Invoke(TableName.Name);
        if (newTableName.IsNotNullOrWhiteSpace()) TableName.SetName(newTableName);
        return this;
    }
    public InsertFromSelectBuilder<T> AsTableIf(bool condition, Func<string, string> func)
        => AsTableIf(condition, func);
    public InsertFromSelectBuilder<T> AsTable(Action<TableName> func)
    {
        func?.Invoke(TableName);
        return this;
    }
    public InsertFromSelectBuilder<T> AsTableIf(bool condition, Action<TableName> func)
        => condition ? AsTable(func) : this;
    #endregion
    #region 复写超时设置
    public override InsertFromSelectBuilder<T> CommandTimeout(int timeoutSeconds) => base.CommandTimeout(timeoutSeconds) as InsertFromSelectBuilder<T>;
    public override InsertFromSelectBuilder<T> CommandTimeoutIf(bool condition, int timeoutSeconds)
        => condition ? CommandTimeout(timeoutSeconds) : this;
    #endregion

    #region ToSql & 执行
    /// <summary>
    /// 注意: type 只能是 EnumInsertToSql.ExecuteAffrows 或 EnumInsertToSql.ExecuteInserted, 不支持 EnumInsertToSql.ExecuteIdentity.
    /// </summary>
    public string ToSql(EnumInsertToSql? type = null)
    {
        type ??= EnumInsertToSql.ExecuteAffrows;
        var selectSql = selectBuilder.ToSqlList(expression);
        var props = propNames.Select(prop => this.EntityInfo.Props.First(i => i.PropNamePure == prop)).ToList();
        var cols = props.Select(prop => prop.ColumnNameQuoted).ToStringSeparated(",");
        var _sql = string.Empty;
        if (type == EnumInsertToSql.ExecuteAffrows)
        {
            _sql = $"insert into {TableName.FullNameQuoted}({cols}) {selectSql}";
        }
        else if (type == EnumInsertToSql.ExecuteInserted)
        {
            var allProps = this.EntityInfo.Props.Where(i => i.CanMapToTable && !i.IsIgnoreSelect).ToList();
            _sql = $"insert into {TableName.FullNameQuoted}({cols}) {selectSql}\r\nselect {allProps.Select(i => i.SelectFunc("t")).ToStringSeparated(",")} from {TableName.FullNameQuoted} t";
        }
        else
        {
            throw new Exception("insert from 不支持返回自增生成的id");
        }
        return _sql;
    }

    /// <summary>
    /// 插入并返回受影响的行数
    /// </summary>
    public virtual int ExecuteAffrows()
    {
        var sql = ToSql();
        return RunWriteMonitor(new AfterWriteArgument
        {
            EntityInfo = EntityInfo,
            TableName = TableName.FullNameQuoted,
            WriteType = EnumWriteType.InsertFromSelect
        }, () => db.ExecuteSql(sql, CommandType.Text, TimeoutSeconds));
    }

    /// <summary>
    /// 插入并返回受影响的行数
    /// </summary>
    public virtual async Task<int> ExecuteAffrowsAsync(CancellationToken cancellationToken = default)
    {
        var sql = ToSql();
        return await RunWriteMonitorAsync(new AfterWriteArgument
        {
            TableName = TableName.FullNameQuoted,
            WriteType = EnumWriteType.InsertFromSelect
        }, async () => await db.ExecuteSqlAsync(sql, CommandType.Text, TimeoutSeconds, null, cancellationToken));
    }

    /// <summary>
    /// 插入并返回新插入的数据
    /// </summary>
    public virtual int ExecuteInserted()
    {
        var sql = ToSql(EnumInsertToSql.ExecuteInserted);
        return RunWriteMonitor(new AfterWriteArgument
        {
            EntityInfo = EntityInfo,
            TableName = TableName.FullNameQuoted,
            WriteType = EnumWriteType.InsertFromSelect
        }, () => db.ExecuteSql(sql, CommandType.Text, TimeoutSeconds));
    }

    /// <summary>
    /// 插入并返回受影响的行数
    /// </summary>
    public virtual async Task<int> ExecuteInsertedAsync(CancellationToken cancellationToken = default)
    {
        var sql = ToSql(EnumInsertToSql.ExecuteInserted);
        return await RunWriteMonitorAsync(new AfterWriteArgument
        {
            TableName = TableName.FullNameQuoted,
            WriteType = EnumWriteType.InsertFromSelect
        }, async () => await db.ExecuteSqlAsync(sql, CommandType.Text, TimeoutSeconds, null, cancellationToken));
    }
    #endregion
}